<!DOCTYPE html>
<html lang="zh">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>批量测试所有 DBC 文件</title>
    <style>
        body { font-family: Arial, sans-serif; margin: 20px; }
        .header { background: #f0f8ff; padding: 20px; border-radius: 10px; margin-bottom: 20px; }
        .controls { margin: 20px 0; }
        .progress { background: #f0f0f0; border-radius: 10px; overflow: hidden; margin: 10px 0; height: 30px; }
        .progress-bar { background: #4CAF50; height: 100%; transition: width 0.3s; text-align: center; line-height: 30px; color: white; font-weight: bold; }
        .results { margin: 20px 0; }
        .file-result { margin: 5px 0; padding: 10px; border-radius: 5px; display: flex; justify-content: space-between; align-items: center; }
        .success { background: #d4edda; border-left: 4px solid #28a745; }
        .failure { background: #f8d7da; border-left: 4px solid #dc3545; }
        .testing { background: #fff3cd; border-left: 4px solid #ffc107; }
        .summary { background: #e9ecef; padding: 20px; border-radius: 10px; margin: 20px 0; }
        .error-detail { font-size: 0.9em; color: #666; margin-top: 5px; }
        .stats { display: flex; gap: 20px; margin: 20px 0; }
        .stat-item { background: #ffffff; padding: 15px; border-radius: 8px; border: 1px solid #ddd; text-align: center; }
        .log { background: #f8f9fa; padding: 15px; border-radius: 5px; font-family: monospace; font-size: 12px; max-height: 400px; overflow-y: auto; }
        .filter { margin: 10px 0; }
        .filter button { margin: 5px; padding: 8px 15px; border: none; border-radius: 5px; cursor: pointer; }
        .filter button.active { background: #007bff; color: white; }
        .filter button:not(.active) { background: #e9ecef; }
    </style>
</head>
<body>
    <div class="header">
        <h1>批量测试所有 DBC 文件</h1>
        <p>自动化测试 opendbc/opendbc/dbc 目录下的所有 DBC 文件，检测兼容性问题</p>
    </div>

    <div class="controls">
        <button id="start-test" onclick="startBatchTest()">开始批量测试</button>
        <button id="stop-test" onclick="stopTest()" disabled>停止测试</button>
        <button onclick="exportResults()">导出结果</button>
    </div>

    <div class="progress">
        <div class="progress-bar" id="progress-bar" style="width: 0%;">0%</div>
    </div>

    <div class="stats" id="stats" style="display: none;">
        <div class="stat-item">
            <div>总文件数</div>
            <div id="total-files">0</div>
        </div>
        <div class="stat-item">
            <div>测试完成</div>
            <div id="tested-files">0</div>
        </div>
        <div class="stat-item">
            <div>成功解析</div>
            <div id="success-files">0</div>
        </div>
        <div class="stat-item">
            <div>解析失败</div>
            <div id="failed-files">0</div>
        </div>
        <div class="stat-item">
            <div>成功率</div>
            <div id="success-rate">0%</div>
        </div>
    </div>

    <div class="filter" id="filter-section" style="display: none;">
        <button class="active" onclick="filterResults('all')">全部</button>
        <button onclick="filterResults('success')">成功</button>
        <button onclick="filterResults('failure')">失败</button>
    </div>

    <div class="results" id="results"></div>

    <div class="summary" id="summary" style="display: none;">
        <h3>错误类型统计</h3>
        <div id="error-stats"></div>
        
        <h3>详细错误日志</h3>
        <div class="log" id="detailed-log"></div>
    </div>

    <script src="dbc-parser.js"></script>
    <script>
        let testResults = [];
        let isTestingRunning = false;
        let currentTestIndex = 0;
        
        // DBC文件列表
        const dbcFiles = [
            'acura_ilx_2016_nidec.dbc',
            'bmw_e9x_e8x.dbc',
            'cadillac_ct6_chassis.dbc',
            'cadillac_ct6_object.dbc',
            'cadillac_ct6_powertrain.dbc',
            'chrysler_cusw.dbc',
            'chrysler_pacifica_2017_hybrid_private_fusion.dbc',
            'comma_body.dbc',
            'ESR.dbc',
            'fca_giorgio.dbc',
            'FORD_CADS.dbc',
            'FORD_CADS_64.dbc',
            'ford_cgea1_2_bodycan_2011.dbc',
            'ford_cgea1_2_ptcan_2011.dbc',
            'ford_fusion_2018_adas.dbc',
            'ford_fusion_2018_pt.dbc',
            'ford_lincoln_base_pt.dbc',
            'gm_global_a_chassis.dbc',
            'gm_global_a_high_voltage_management.dbc',
            'gm_global_a_lowspeed.dbc',
            'gm_global_a_lowspeed_1818125.dbc',
            'gm_global_a_object.dbc',
            'gm_global_a_powertrain_expansion.dbc',
            'gwm_haval_h6_phev_2024.dbc',
            'hongqi_hs5.dbc',
            'hyundai_2015_ccan.dbc',
            'hyundai_2015_mcan.dbc',
            'hyundai_i30_2014.dbc',
            'hyundai_kia_generic.dbc',
            'hyundai_santafe_2007.dbc',
            'luxgen_s5_2015.dbc',
            'mazda_2017.dbc',
            'mazda_3_2019.dbc',
            'mazda_radar.dbc',
            'mazda_rx8.dbc',
            'mercedes_benz_e350_2010.dbc',
            'nissan_xterra_2011.dbc',
            'opel_omega_2001.dbc',
            'rivian_park_assist_can.dbc',
            'rivian_primary_actuator.dbc',
            'tesla_can.dbc',
            'tesla_model3_party.dbc',
            'tesla_model3_vehicle.dbc',
            'tesla_powertrain.dbc',
            'toyota_2017_ref_pt.dbc',
            'toyota_adas.dbc',
            'toyota_iQ_2009_can.dbc',
            'toyota_prius_2010_pt.dbc',
            'toyota_radar_dsu_tssp.dbc',
            'toyota_tss2_adas.dbc',
            'volvo_v40_2017_pt.dbc',
            'volvo_v60_2015_pt.dbc',
            'vw_meb.dbc',
            'vw_mqb.dbc',
            'vw_mqbevo.dbc',
            'vw_pq.dbc'
        ];

        function updateProgress(current, total) {
            const percent = total > 0 ? Math.round((current / total) * 100) : 0;
            const progressBar = document.getElementById('progress-bar');
            progressBar.style.width = percent + '%';
            progressBar.textContent = `${percent}% (${current}/${total})`;
        }

        function updateStats() {
            const total = testResults.length;
            const tested = testResults.filter(r => r.status !== 'testing').length;
            const success = testResults.filter(r => r.status === 'success').length;
            const failed = testResults.filter(r => r.status === 'failure').length;
            const successRate = tested > 0 ? Math.round((success / tested) * 100) : 0;

            document.getElementById('total-files').textContent = total;
            document.getElementById('tested-files').textContent = tested;
            document.getElementById('success-files').textContent = success;
            document.getElementById('failed-files').textContent = failed;
            document.getElementById('success-rate').textContent = successRate + '%';
        }

        function displayResults() {
            const resultsDiv = document.getElementById('results');
            resultsDiv.innerHTML = testResults.map(result => {
                let className = result.status;
                let statusText = '';
                let errorDetail = '';

                switch (result.status) {
                    case 'success':
                        statusText = `✓ 成功 (${result.parseTime}ms, ${result.frames}帧, ${result.warnings}警告)`;
                        break;
                    case 'failure':
                        statusText = `✗ 失败`;
                        errorDetail = `<div class="error-detail">${result.error}</div>`;
                        break;
                    case 'testing':
                        statusText = `⏳ 测试中...`;
                        break;
                }

                return `
                    <div class="file-result ${className}" data-status="${result.status}">
                        <div>
                            <strong>${result.filename}</strong> (${result.size})
                            ${errorDetail}
                        </div>
                        <div>${statusText}</div>
                    </div>
                `;
            }).join('');
        }

        async function testSingleFile(filename) {
            try {
                const response = await fetch(`../opendbc/opendbc/dbc/${filename}`);
                if (!response.ok) {
                    throw new Error(`HTTP ${response.status}: ${response.statusText}`);
                }

                const content = await response.text();
                const size = `${(content.length / 1024).toFixed(1)}KB`;
                
                const startTime = performance.now();
                const database = parseDBCContent(content, filename);
                const endTime = performance.now();
                const parseTime = Math.round(endTime - startTime);

                return {
                    filename,
                    status: 'success',
                    size,
                    parseTime,
                    frames: database.frames.length,
                    signals: database.frames.reduce((sum, frame) => sum + frame.signals.length, 0),
                    warnings: database.warnings.length,
                    warningList: database.warnings
                };
            } catch (error) {
                return {
                    filename,
                    status: 'failure',
                    size: 'Unknown',
                    error: error.message,
                    errorType: categorizeError(error.message)
                };
            }
        }

        function categorizeError(errorMessage) {
            if (errorMessage.includes('VAL_TABLE_')) return 'VAL_TABLE_语法';
            if (errorMessage.includes('意外的令牌')) return '语法解析错误';
            if (errorMessage.includes('HTTP')) return '文件加载错误';
            if (errorMessage.includes('重复')) return '重复定义';
            if (errorMessage.includes('期望')) return '语法期望错误';
            return '其他错误';
        }

        async function startBatchTest() {
            if (isTestingRunning) return;
            
            isTestingRunning = true;
            testResults = [];
            currentTestIndex = 0;
            
            document.getElementById('start-test').disabled = true;
            document.getElementById('stop-test').disabled = false;
            document.getElementById('stats').style.display = 'flex';
            document.getElementById('filter-section').style.display = 'block';

            // 初始化测试结果
            testResults = dbcFiles.map(filename => ({
                filename,
                status: 'testing',
                size: 'Unknown'
            }));

            displayResults();
            updateStats();

            // 逐个测试文件
            for (let i = 0; i < dbcFiles.length && isTestingRunning; i++) {
                currentTestIndex = i;
                updateProgress(i, dbcFiles.length);
                
                console.log(`测试文件 ${i + 1}/${dbcFiles.length}: ${dbcFiles[i]}`);
                const result = await testSingleFile(dbcFiles[i]);
                testResults[i] = result;
                
                displayResults();
                updateStats();
                
                // 给UI一些时间更新
                await new Promise(resolve => setTimeout(resolve, 50));
            }

            updateProgress(dbcFiles.length, dbcFiles.length);
            isTestingRunning = false;
            document.getElementById('start-test').disabled = false;
            document.getElementById('stop-test').disabled = true;

            // 显示总结
            showSummary();
        }

        function stopTest() {
            isTestingRunning = false;
            document.getElementById('start-test').disabled = false;
            document.getElementById('stop-test').disabled = true;
        }

        function filterResults(type) {
            // 更新按钮状态
            document.querySelectorAll('.filter button').forEach(btn => {
                btn.classList.remove('active');
            });
            event.target.classList.add('active');

            // 过滤结果
            const allResults = document.querySelectorAll('.file-result');
            allResults.forEach(result => {
                const status = result.dataset.status;
                if (type === 'all' || status === type) {
                    result.style.display = 'flex';
                } else {
                    result.style.display = 'none';
                }
            });
        }

        function showSummary() {
            const failedResults = testResults.filter(r => r.status === 'failure');
            const errorTypes = {};
            
            failedResults.forEach(result => {
                const type = result.errorType || '未分类';
                errorTypes[type] = (errorTypes[type] || 0) + 1;
            });

            const errorStatsHtml = Object.entries(errorTypes)
                .sort((a, b) => b[1] - a[1])
                .map(([type, count]) => `<div>${type}: ${count} 个文件</div>`)
                .join('');

            const detailedLogHtml = failedResults
                .map(result => `<div><strong>${result.filename}:</strong> ${result.error}</div>`)
                .join('');

            document.getElementById('error-stats').innerHTML = errorStatsHtml;
            document.getElementById('detailed-log').innerHTML = detailedLogHtml;
            document.getElementById('summary').style.display = 'block';
        }

        function exportResults() {
            const csvContent = [
                ['文件名', '状态', '大小', '解析时间(ms)', '帧数', '信号数', '警告数', '错误信息'].join(','),
                ...testResults.map(r => [
                    r.filename,
                    r.status,
                    r.size || '',
                    r.parseTime || '',
                    r.frames || '',
                    r.signals || '',
                    r.warnings || '',
                    (r.error || '').replace(/,/g, ';')
                ].join(','))
            ].join('\n');

            const blob = new Blob([csvContent], { type: 'text/csv;charset=utf-8;' });
            const link = document.createElement('a');
            link.href = URL.createObjectURL(blob);
            link.download = 'dbc_test_results.csv';
            link.click();
        }
    </script>
</body>
</html> 